package com.syh.jn.scm.service.impl.supplier.purchase;

import cn.redscarf.commons.util.bean.ExtBeanUtils;
import cn.redscarf.commons.util.other.ExtAssert;
import cn.redscarf.commons.util.other.ExtStringUtils;
import cn.redscarf.commons.spring.SpringContextUtils;
import cn.redscarf.commons.wrapper.Restrictions;
import cn.redscarf.commons.wrapper.Wrapper;
import com.alibaba.dubbo.config.annotation.Service;
import com.syh.core.ddd.event.annotation.DomainEventHandle;
import com.syh.core.ddd.infrastructure.repository.AggregateRepository;
import com.syh.core.ddd.service.AbstractServiceImpl;
import com.syh.jn.scm.domain.operation.entity.supplier.OperaSupplier;
import com.syh.jn.scm.domain.operation.event.supplier.OperaSupplierEvent;
import com.syh.jn.scm.domain.supplier.entity.finance.OrderExpensesBill;
import com.syh.jn.scm.domain.supplier.entity.finance.StatementAccountBill;
import com.syh.jn.scm.domain.supplier.entity.logistics.LogisticsBill;
import com.syh.jn.scm.domain.supplier.entity.logistics.LogisticsBillItem;
import com.syh.jn.scm.domain.supplier.entity.purchase.PurchaseOrder;
import com.syh.jn.scm.domain.supplier.entity.purchase.PurchaseOrderItem;
import com.syh.jn.scm.domain.supplier.entity.purchase.PurchaseReturnOrder;
import com.syh.jn.scm.domain.supplier.event.PurchaseOrderEvent;
import com.syh.jn.scm.domain.supplier.event.PurchaseReturnOrderEvent;
import com.syh.jn.scm.domain.supplier.event.finance.BusinessAccountRecordEvent;
import com.syh.jn.scm.domain.supplier.event.finance.OrderExpensesBillEvent;
import com.syh.jn.scm.domain.supplier.event.finance.StatementAccountBillEvent;
import com.syh.jn.scm.domain.supplier.repository.logistics.LogisticsBillReadRepository;
import com.syh.jn.scm.domain.supplier.repository.purchase.PurchaseOrderReadRepository;
import com.syh.jn.scm.domain.supplier.repository.purchase.PurchaseOrderRepository;
import com.syh.jn.scm.dto.supplier.purchase.*;
import com.syh.jn.scm.dto.support.FilesDto;
import com.syh.jn.scm.service.api.supplier.finance.enums.OrderExpensesBillEnums;
import com.syh.jn.scm.service.api.supplier.finance.enums.StatementAccountBillEnum;
import com.syh.jn.scm.service.api.supplier.logistics.enums.LogisticsBillEnums;
import com.syh.jn.scm.service.api.supplier.purchase.PurchaseOrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
@Transactional
public class PurchaseOrderServiceImpl extends AbstractServiceImpl<PurchaseOrderSto, PurchaseOrderMto, PurchaseOrderVo, PurchaseOrder, Long> implements PurchaseOrderService {
  @Autowired
  private PurchaseOrderRepository purchaseOrderRepository;

  @Resource(name = "purchaseOrderReadRepositoryMbImpl")
  private PurchaseOrderReadRepository purchaseOrderReadRepositoryMbImpl;

  @Override
  protected AggregateRepository<PurchaseOrder, Long> getAggregateRepository() {
    return null;//TODO 继承AbstractServiceImpl类是为了抛出事件
  }

  @Override
  public PurchaseOrderVo getById(Long id) {
    return this.purchaseOrderReadRepositoryMbImpl.getById(id);
  }

  @Override
  public boolean contains(Long id) {
    return this.purchaseOrderReadRepositoryMbImpl.contains(id);
  }

  @Override
  public PurchaseOrderVo getOne(Wrapper wrapper) {
    return this.purchaseOrderReadRepositoryMbImpl.getOne(wrapper);
  }

  @Override
  public List<PurchaseOrderVo> findList(Wrapper wrapper) {
    return this.purchaseOrderReadRepositoryMbImpl.getList(wrapper);
  }

  @Override
  public List<PurchaseOrderVo> findListPage(Wrapper wrapper, Integer startRow, Integer count) {
    return this.purchaseOrderReadRepositoryMbImpl.findListPage(wrapper, startRow, count);
  }

  @Override
  public int getCount(Wrapper wrapper) {
    return this.purchaseOrderReadRepositoryMbImpl.getCount(wrapper);
  }

  @Override
  public int getCountView(Wrapper wrapper) {
    return this.purchaseOrderReadRepositoryMbImpl.getCount(wrapper);
  }

  /**
   * 新增采购订单
   *
   * @param sto
   * @return
   */
  @Override
  public Long save(PurchaseOrderSto sto) {
    PurchaseOrder order = new PurchaseOrder(sto);
    return purchaseOrderRepository.add(order);
  }

  /**
   * 修改采购订单
   *
   * @param mto
   */
  @Override
  public void update(PurchaseOrderMto mto) {
    PurchaseOrder order = idIsNull(mto.getId());//判空
    order.modify(mto);
    purchaseOrderRepository.update(order);
  }

  /**
   * 根据id删除采购订单
   *
   * @param id
   */
  @Override
  public void delete(Long id) {
    PurchaseOrder order = idIsNull(id);//判空
    order.destroy();
    purchaseOrderRepository.remove(order);
    publicEvent(PurchaseOrderEvent.buildPurchaseOrderDestoryEvent(new Object[]{order}));
  }

  /**
   * 根据采购订单id查出明细
   *
   * @param id
   * @return
   */
  @Override
  public List<PurchaseOrderItemVo> getItemsByPurchaseOrderId(Long id) {
    return this.purchaseOrderReadRepositoryMbImpl.getItemsByPurchaseOrderId(id);
  }

  /**
   * 根据采购订单id查出明细
   *
   * @param ids
   * @return
   */
  @Override
  public List<PurchaseOrderItemVo> getItemsByPurchaseOrderIds(Long[] ids) {
    return this.purchaseOrderReadRepositoryMbImpl.getItemsByPurchaseOrderIds(ids);
  }

  /**
   * 根据商品id拿到订单明细商品
   *
   * @param goodsId
   * @return
   */
  @Override
  public PurchaseOrderItemVo getByGoodsId(Long goodsId, Long id) {
    return this.purchaseOrderReadRepositoryMbImpl.getByGoodsId(goodsId, id);
  }

  /**
   * 确认订单
   *
   * @param id 订单id
   */
  @Override
  public void confirmOrder(Long id) {
    PurchaseOrder purchaseOrder = this.idIsNull(id);
    purchaseOrder.confirm();
    purchaseOrderRepository.update(purchaseOrder, false);
  }

  /**
   * 取消订单
   *
   * @param id
   */
  @Override
  public void cancel(Long id) {
    PurchaseOrder purchaseOrder = this.idIsNull(id);
    purchaseOrder.cancel();
    purchaseOrderRepository.update(purchaseOrder, false);
    publicEvent(PurchaseOrderEvent.buildPurchaseOrderCancelEvent(new Object[]{purchaseOrder}));
  }

  /**
   * 判空
   *
   * @param id
   * @return
   */
  private PurchaseOrder idIsNull(Long id) {
    ExtAssert.notNull(id, "id不能为空");
    PurchaseOrder entity = purchaseOrderRepository.getById(id);
    ExtAssert.notNull(entity, "订单不存在");
    return entity;
  }

  /**
   * 上传单据图片
   *
   * @param dto
   */
  @Override
  public void uploadImages(FilesDto dto) {
    ExtAssert.notNull(dto.getId(), "id不能为空");
    PurchaseOrder order = purchaseOrderRepository.getById(dto.getId());
    order.uploadImages(dto);
    purchaseOrderRepository.upload(order);
  }

  /**
   * 批量保存
   *
   * @param mapStoList
   * @return
   */
  @Override
  public List<Long> batchSaveWithVirtualId(List<Map<String, String>> mapStoList) {
    List<Long> ids = new ArrayList<>();
    PurchaseOrderSto purchaseOrderSto = new PurchaseOrderSto();
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
    List<PurchaseOrderSto> orderStoList = new ArrayList<>();
    Long businessEntityId = Long.valueOf(mapStoList.get(0).get("businessEntityId"));
    Long inputTime = null;
    for (Map<String, String> orderSto : mapStoList) {
      if (ExtStringUtils.isBlank(orderSto.get("goodsName")) && !ExtStringUtils.isBlank(orderSto.get("code"))) {
        try {
          inputTime = simpleDateFormat.parse(orderSto.get("inputDateTimestamp")).getTime();
        } catch (ParseException e) {
          e.printStackTrace();
        }
        purchaseOrderSto = PurchaseOrderSto.builder()
                .businessEntityId(businessEntityId)
                .code(orderSto.get("code"))
                .extCode(orderSto.get("code"))
                .orderType(Short.valueOf("0"))
                .upStreamCompanyId(Long.valueOf(orderSto.get("upStreamCompanyId")))
                .deliName(orderSto.get("deliName"))
                .inputDateTimestamp(inputTime)
                .deliTel(orderSto.get("deliTel"))
                .deliArea(orderSto.get("deliArea"))
                .deliAddress(orderSto.get("deliAddress"))
                .discount(BigDecimal.valueOf(Double.valueOf(orderSto.get("discount"))))
                .totalFreight(BigDecimal.valueOf(Double.valueOf(orderSto.get("totalFreight"))))
                .remark(orderSto.get("remarkCode") + orderSto.get("abstract") + orderSto.get("remark"))
                .build();
        orderStoList.add(purchaseOrderSto);
      }
    }
    orderStoList.forEach(orderSto -> {
      Long realId = this.save(this.putItem(orderSto, mapStoList));
      ids.add(realId);
    });
    return ids;
  }

  public PurchaseOrderSto putItem(PurchaseOrderSto orderSto, List<Map<String, String>> mapStoList) {
    List<PurchaseOrderSto.PurchaseOrderItemSTO> itemSTOList = new ArrayList<>();
    for (Map<String, String> item : mapStoList) {
      if (!ExtStringUtils.isBlank(item.get("goodsName"))) {
        PurchaseOrderSto.PurchaseOrderItemSTO itemSto = orderSto.new PurchaseOrderItemSTO();
        itemSto.setPurchaseOrderCode(item.get("purchaseOrderCode"));
        itemSto.setGoodsId(Long.valueOf(item.get("goodsId")));
        itemSto.setQuantity(BigDecimal.valueOf(Double.valueOf(item.get("quantity"))));
        itemSto.setPrice(BigDecimal.valueOf(Double.valueOf(item.get("price"))));
        itemSto.setRemark(item.get("remark"));
        if (itemSto.getPurchaseOrderCode().equals(orderSto.getCode())) {
          itemSTOList.add(itemSto);
          orderSto.setItems(itemSTOList);
        }
      }
    }
    orderSto.setCode(null);
    return orderSto;
  }

  /**
   * 消费者：监听采购退货单创建（记录已生成退货数量）
   *
   * @param returnOrder
   */
  @DomainEventHandle(PurchaseReturnOrderEvent.PURCHASE_RETURN_ORDER_CREATED)
  protected void handlePurchaseReturnOrderCreated(PurchaseReturnOrder returnOrder) {
    PurchaseOrder order = purchaseOrderRepository.getById(returnOrder.getPurchaseOrderId());
    order.createdReturnOrder(returnOrder);
    purchaseOrderRepository.updateOrderAndItems(order);
  }

  /**
   * 消费者：监听采购退货单修改（记录已生成退货数量）
   *
   * @param returnOrder
   */
  @DomainEventHandle(PurchaseReturnOrderEvent.PURCHASE_RETURN_ORDER_UPDATED)
  protected void handlePurchaseReturnOrderUpdated(PurchaseReturnOrder returnOrder, PurchaseReturnOrder oldReturnOrder) {
    PurchaseOrder order = purchaseOrderRepository.getById(returnOrder.getPurchaseOrderId());
    order.updatedReturnOrder(returnOrder, oldReturnOrder);
    purchaseOrderRepository.updateOrderAndItems(order);
  }

  /**
   * 消费者：监听采购退货单取消（记录已生成退货数量）
   *
   * @param returnOrder
   */
  @DomainEventHandle(PurchaseReturnOrderEvent.PURCHASE_RETURN_ORDER_CANCEL)
  protected void handlePurchaseReturnOrderCancel(PurchaseReturnOrder returnOrder) {
    PurchaseOrder order = purchaseOrderRepository.getById(returnOrder.getPurchaseOrderId());
    order.cancelReturnOrder(returnOrder);
    purchaseOrderRepository.updateOrderAndItems(order);
  }

  /**
   * 消费者：监听对账单（生成对账单状态和对账单号）
   *
   * @param bill
   */
  @DomainEventHandle(StatementAccountBillEvent.STATEMENT_BILL_ACCOUNT_CREATED)
  protected void handleStatementBillAccountCreated(StatementAccountBill bill) {
    bill.getStatementAccountBillItems().forEach(billItem -> {
      if (billItem.getSourceType().equals(StatementAccountBillEnum.SOURCE_PURCHASE_ORD.getStatus())) {
        PurchaseOrder purchaseOrder = purchaseOrderRepository.getById(billItem.getSourceId());
        purchaseOrder.generating(bill.getId());
        purchaseOrderRepository.update(purchaseOrder, false);
      }
    });
  }

  /**
   * 消费者：监听对账单（删除对账单状态和对账单号）
   *
   * @param bill
   */
  @DomainEventHandle(StatementAccountBillEvent.STATEMENT_BILL_ACCOUNT_UPDATA)
  protected void handleStatementBillAccountUpdata(StatementAccountBill bill) {
    Wrapper wrapper = Wrapper.build();
    wrapper.append(Restrictions.eq("statementId", bill.getId()));
    List<PurchaseOrder> purchaseOrders = purchaseOrderRepository.getList(wrapper);
    purchaseOrders.forEach(purchaseOrder -> {
      purchaseOrder.cancelStatementState();
      purchaseOrderRepository.update(purchaseOrder, false);
    });
    bill.getStatementAccountBillItems().forEach(billItem -> {
      if (billItem.getSourceType().equals(StatementAccountBillEnum.SOURCE_PURCHASE_ORD.getStatus())) {
        PurchaseOrder purchaseOrder = purchaseOrderRepository.getById(billItem.getSourceId());
        purchaseOrder.generating(bill.getId());
        purchaseOrderRepository.update(purchaseOrder, false);
      }
    });
  }

  /**
   * 消费者：监听对账单（删除对账单状态和对账单号）
   *
   * @param bill
   */
  @DomainEventHandle(StatementAccountBillEvent.STATEMENT_BILL_ACCOUNT_DESTROY)
  protected void handleStatementBillAccountDestroy(StatementAccountBill bill) {
    bill.getStatementAccountBillItems().forEach(billItem -> {
      if (billItem.getSourceType().equals(StatementAccountBillEnum.SOURCE_PURCHASE_ORD.getStatus())) {
        PurchaseOrder purchaseOrder = purchaseOrderRepository.getById(billItem.getSourceId());
        purchaseOrder.cancelStatementState();
        purchaseOrderRepository.update(purchaseOrder, false);
      }
    });
  }

  /**
   * 消费者：监听物流单收货（修改发货数量）
   */
  @DomainEventHandle(BusinessAccountRecordEvent.BUSINESS_ACCOUNT_RECORD_CHECK_DISCOUNT_AFTER)
  protected void handleLogisticsBillCollected(LogisticsBill bill) {
    if (LogisticsBillEnums.OrderType.PURCHASE.getStatus().equals(bill.getOrderType())) {
      PurchaseOrder order = purchaseOrderRepository.getById(bill.getOrderId());
      order.collect(bill);
      purchaseOrderRepository.updateOrderAndItems(order);
    }
  }

  /**
   * 消费者：监听物流单取消收货（修改发货数量）
   */
  @DomainEventHandle(BusinessAccountRecordEvent.BUSINESS_ACCOUNT_RECORD_CHECK_DISCOUNT_BEFORE)
  protected void handleLogisticsBillUnReceived(LogisticsBill bill) {
    if (LogisticsBillEnums.OrderType.PURCHASE.getStatus().equals(bill.getOrderType())) {
      PurchaseOrder order = purchaseOrderRepository.getById(bill.getOrderId());
      order.cancelCollect(bill);
      purchaseOrderRepository.updateOrderAndItems(order);
    }
  }


  /**
   * 消费者：监听订单费用表创建、更新事件（记录费用表id）
   *
   * @param orderExpensesBill
   */
  @DomainEventHandle({OrderExpensesBillEvent.ORDER_EXPENSES_BILL_CREATED, OrderExpensesBillEvent.ORDER_EXPENSES_BILL_UPDATED})
  protected void handleOrderExpensesBillCreatedOrUpdated(OrderExpensesBill orderExpensesBill) {
    if (orderExpensesBill.getOrderType().equals(OrderExpensesBillEnums.OrderType.PURCHASE_ORDER.getStatus())) {
      PurchaseOrder purchaseOrder = this.purchaseOrderRepository.getById(orderExpensesBill.getSourceId());
      purchaseOrder.addExpensesBill(orderExpensesBill.getId());
      this.purchaseOrderRepository.update(purchaseOrder, false);
    }
  }

  /**
   * 消费者：监听订单费用表删除事件（删除订单费用信息）
   *
   * @param orderExpensesBill
   */
  @DomainEventHandle(OrderExpensesBillEvent.ORDER_EXPENSES_BILL_DELETED)
  protected void handleOrderExpensesBillDeleted(OrderExpensesBill orderExpensesBill) {
    if (orderExpensesBill.getOrderType().equals(OrderExpensesBillEnums.OrderType.PURCHASE_ORDER.getStatus())) {
      PurchaseOrder purchaseOrder = this.purchaseOrderRepository.getById(orderExpensesBill.getSourceId());
      purchaseOrder.deletedExpensesBill(orderExpensesBill.getId());
      this.purchaseOrderRepository.update(purchaseOrder, false);
    }
  }

  /**
   * 消费者：监听费用表确认事件（计算订单费用总额）
   *
   * @param orderExpensesBill
   */
  @DomainEventHandle(OrderExpensesBillEvent.ORDER_EXPENSES_BILL_CONFIRM)
  protected void handleOrderExpensesBillConfirm(OrderExpensesBill orderExpensesBill) {
    if (orderExpensesBill.getOrderType().equals(OrderExpensesBillEnums.OrderType.PURCHASE_ORDER.getStatus())) {
      PurchaseOrder purchaseOrder = this.purchaseOrderRepository.getById(orderExpensesBill.getSourceId());
      purchaseOrder.confirmExpensesBill(orderExpensesBill);
      this.purchaseOrderRepository.update(purchaseOrder, false);
    }
  }

  /**
   * 消费者：监听费用表取消事件（计算订单费用总额）
   *
   * @param orderExpensesBill
   * @param oldBill
   */
  @DomainEventHandle(OrderExpensesBillEvent.ORDER_EXPENSES_BILL_CANCEL)
  protected void handleOrderExpensesBillCancel(OrderExpensesBill orderExpensesBill, OrderExpensesBill oldBill) {
    if (orderExpensesBill.getOrderType().equals(OrderExpensesBillEnums.OrderType.PURCHASE_ORDER.getStatus())) {
      PurchaseOrder purchaseOrder = this.purchaseOrderRepository.getById(orderExpensesBill.getSourceId());
      purchaseOrder.cancelExpensesBill(oldBill);
      this.purchaseOrderRepository.update(purchaseOrder, false);
    }
  }

  /**
   * 消费者：监听服务商反开账（删除所有该服务商的采购单）
   */
  @DomainEventHandle(OperaSupplierEvent.SUPPLIER_REVERSAL_ACCOUNT)
  protected void handleSupplierReversalAccount(OperaSupplier supplier) {
    this.purchaseOrderRepository.deleteByBusinessEntityId(supplier.getId());
  }

  /**
   * 纠正订单物流相关字段数据
   */
  @Override
  public void correctingLogisticsData() {
    //获取所有采购订单
    Wrapper we = Wrapper.build();
    List<PurchaseOrder> purchaseOrders = purchaseOrderRepository.getList(we);
    Long[] purchaseOrdersIds = new Long[purchaseOrders.size()];
    //获取所有未发货的采购物流订单
    LogisticsBillReadRepository logisticsBillReadRepository = (LogisticsBillReadRepository) SpringContextUtils.getBean(LogisticsBillReadRepository.class);
    Wrapper logisticsWe = Wrapper.build().append(Restrictions.eq("orderType", LogisticsBillEnums.OrderType.PURCHASE.getStatus()))
            .append(Restrictions.eq("logisticsState", LogisticsBillEnums.State.UNDELIVERY.getStatus()));
    List<LogisticsBill> logisticsBills = logisticsBillReadRepository.getList(logisticsWe);

    int index = 0;
    //获取采购订单id数组
    for (PurchaseOrder purchaseOrder : purchaseOrders) {
      purchaseOrdersIds[index++] = purchaseOrder.getId();
      purchaseOrder.setItems(new ArrayList<PurchaseOrderItem>());
    }
    //根据采购订单id数组获取采购订单项
    List<PurchaseOrderItem> purchaseOrderItems = purchaseOrderRepository.getItemsByPurchaseOrderIds(purchaseOrdersIds);
    Long[] logisticsBillIds = new Long[logisticsBills.size()];
    index = 0;
    for (LogisticsBill logisticsBill : logisticsBills) {
      logisticsBillIds[index++] = logisticsBill.getId();
      logisticsBill.setItems(new ArrayList<LogisticsBillItem>());
    }
    //根据物流单id数组获取物流单项
    List<LogisticsBillItem> logisticsBillItems = logisticsBillIds.length == 0 ? new ArrayList<>() : logisticsBillReadRepository.getItemsByLogisticsBillIds(logisticsBillIds);
    for (LogisticsBill logisticsBill : logisticsBills) {
      //匹配物流单
      logisticsBillItems.forEach(logisticsBillItem -> {
        if (logisticsBillItem.getLogisticsBillId().equals(logisticsBill.getId())) {
          logisticsBill.getItems().add(logisticsBillItem);
        }
      });
    }
    for (PurchaseOrder purchaseOrder : purchaseOrders) {
      //匹配采购订单项
      purchaseOrderItems.forEach(purchaseOrderItem -> {
        if (purchaseOrderItem.getPurchaseOrderId().equals(purchaseOrder.getId())) {
          purchaseOrder.getItems().add(purchaseOrderItem);
        }
      });
      //采购单相关物流单
      List<LogisticsBillItem> purchaseLogisticsBillItems = new ArrayList<>();
      logisticsBills.forEach(logisticsBill -> {
        if (logisticsBill.getOrderId().equals(purchaseOrder.getId())) {
          purchaseLogisticsBillItems.addAll(logisticsBill.getItems());
        }
      });
      purchaseOrder.correctingLogisticsData(purchaseLogisticsBillItems);
      purchaseOrderRepository.updateOrderAndItems(purchaseOrder);
    }
  }


  /**
   * 统计采购金额
   *
   * @param wrapper
   * @return
   */
  @Override
  public PurStatisticalAmountVo statisticalAmount(Wrapper wrapper) {
    return this.purchaseOrderReadRepositoryMbImpl.statisticalAmount(wrapper);
  }

  @Override
  public PurStatisticalAmountVo statisticalAmountByOrder(Wrapper we) {
    return this.purchaseOrderReadRepositoryMbImpl.statisticalAmountByOrder(we);
  }

  /**
   * 内部方法  领域对象转vo
   *
   * @param entityList
   * @return
   */
  private List<PurchaseOrderItemVo> transformPurchase(List<PurchaseOrderItem> entityList) {
    List<PurchaseOrderItemVo> voList = new ArrayList<>();
    entityList.stream().forEach((entity) -> {
      PurchaseOrderItemVo vo = new PurchaseOrderItemVo();
      ExtBeanUtils.copyProperties(entity, vo);
      voList.add(vo);
    });
    return voList;
  }

  /**
   * 领域对象转vo
   *
   * @param entity
   * @return
   */
  private PurchaseOrderItemVo trans(PurchaseOrderItem entity) {
    PurchaseOrderItemVo vo = new PurchaseOrderItemVo();
    ExtBeanUtils.copyProperties(entity, vo);
    return vo;
  }
}
